package enum

import (
	"fmt"
	"reflect"
	"strconv"

	"gitee.com/OneBedOneDream/general/maps"
	"gitee.com/OneBedOneDream/general/mutexs"
)

type EnumInfo struct {
	enum    any
	numbers map[int]any
	names   map[string]any
}

var enumInfos = make(map[reflect.Type]*EnumInfo)
var locker mutexs.Mutex

// 通过只查找Record
func FromNumber[ENUM any](number int) (record *Record[ENUM], ok bool) {
	recordAny, ok := getEnumInfo[ENUM]().numbers[number]
	if !ok {
		return
	}
	record, ok = recordAny.(*Record[ENUM])
	return
}

// 通过名称查找Record
func FromName[ENUM any](name string) (record *Record[ENUM], ok bool) {
	recordAny, ok := getEnumInfo[ENUM]().names[name]
	if !ok {
		return
	}
	record, ok = recordAny.(*Record[ENUM])
	return
}

func getEnumInfo[ENUM any]() (enumInfo *EnumInfo) {
	rt := reflect.TypeOf((*ENUM)(nil)).Elem()
	locker.Lock(func() {
		var ok bool
		enumInfo, ok = enumInfos[rt]
		if !ok {
			enumInfo = new(EnumInfo)
			enum := new(ENUM)
			enumInfo.enum = enum
			enumInfo.numbers = make(map[int]any)
			enumInfo.names = make(map[string]any)
			rv := reflect.ValueOf(enum).Elem()
			rt := rv.Type()
			for i := 0; i < rt.NumField(); i++ {
				numberStr := rt.Field(i).Tag.Get(`number`)
				name := rt.Field(i).Tag.Get(`name`)
				if numberStr == "" || name == "" {
					panic(rt.Name() + `枚举格式错误`)
				} else {
					number, err := strconv.Atoi(numberStr)
					if err != nil {
						panic(rt.Name() + `发现错误的枚举值`)
					}
					if maps.Exist(enumInfo.numbers, number) {
						panic(fmt.Sprintln(rt.Name(), `枚举中发现重复的值`, number))
					}
					if maps.Exist(enumInfo.names, name) {
						panic(fmt.Sprintln(rt.Name(), `枚举中发现重复的名字`, name))
					}
					record := &Record[ENUM]{number, name}
					enumInfo.numbers[number] = record
					enumInfo.names[name] = record
					rv.Field(i).Set(reflect.ValueOf(record))
				}
			}
			enumInfos[rt] = enumInfo
		}
	})
	return
}

func Enum[ENUM any]() (enum *ENUM) {
	enum = getEnumInfo[ENUM]().enum.(*ENUM)
	return
}
